@sagentlab/navarch-runtime 0.1.118 → 0.1.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -815,6 +815,21 @@ to OpenCode's remote-server config with OAuth disabled and environment-backed
815
815
  headers. Missing accounting fields stay absent rather than becoming a
816
816
  fabricated zero.
817
817
 
818
+ Host OpenCode sessions preserve operator provider definitions from
819
+ `$XDG_CONFIG_HOME/opencode/opencode.json(c)` (default
820
+ `~/.config/opencode/opencode.json(c)`), then `OPENCODE_CONFIG`, configs in
821
+ `OPENCODE_CONFIG_DIR`, and finally `OPENCODE_CONFIG_CONTENT`. JSONC comments
822
+ and trailing commas are supported. Nested provider settings merge in that
823
+ order; later arrays replace earlier arrays. Legacy `provider` entries work
824
+ with V1 and V2; native `providers` entries are included for V2. Custom model
825
+ IDs, metadata, and variants are preserved, so a pin such as
826
+ `--model magpie/openrouter/deepseek/deepseek-v4.1-flash-1017` works without
827
+ duplicating the provider configuration. API keys and header values become
828
+ child environment references, and file references resolve relative to their
829
+ original config file. Provider settings are the only imported configuration:
830
+ Navarch still generates the session's MCP and permissions and disables
831
+ project config discovery. Docker sessions do not import host providers.
832
+
818
833
  OpenCode also runs with `--print-logs --log-level ERROR` by default (see its
819
834
  [CLI flags](https://opencode.ai/docs/cli/#global-flags)), so provider errors
820
835
  that are missing from assistant messages still reach the runtime. A rate or
@@ -214,6 +214,73 @@ function isInsideOpeningQuote(command, start) {
214
214
  return beforeQuote === undefined || /[\s=(<>;|&]/.test(beforeQuote);
215
215
  }
216
216
 
217
+ /**
218
+ * Returns [open, close, segmentStart] for the quoted word containing `index`,
219
+ * or null when it sits outside quotes or the quote state cannot be trusted.
220
+ * `segmentStart` follows the last unquoted, unescaped `;`, `|`, `&` or newline
221
+ * before the word, so a separator inside quotes never starts a segment. Only
222
+ * plain single and double quotes are tracked. Constructs that nest or hide
223
+ * quotes (backticks, `$(`, `$'`, heredocs, shell comments) make the scan bail
224
+ * out, so a misread quote never exempts a token.
225
+ */
226
+ function quotedSpanAt(command, index) {
227
+ let quote = "";
228
+ let open = -1;
229
+ let segmentStart = 0;
230
+ for (let i = 0; i < command.length; i++) {
231
+ const ch = command[i];
232
+ if (quote === "'") {
233
+ if (ch === "'") {
234
+ if (index > open && index < i) return [open, i, segmentStart];
235
+ quote = "";
236
+ }
237
+ continue;
238
+ }
239
+ if (ch === "`" || (ch === "$" && (command[i + 1] === "(" || command[i + 1] === "'"))) return null;
240
+ if (ch === "\\") {
241
+ i++;
242
+ continue;
243
+ }
244
+ if (quote === '"') {
245
+ if (ch === '"') {
246
+ if (index > open && index < i) return [open, i, segmentStart];
247
+ quote = "";
248
+ }
249
+ continue;
250
+ }
251
+ if (/[;|&\n]/.test(ch)) segmentStart = i + 1;
252
+ if (ch === "<" && command[i + 1] === "<") return null;
253
+ if (ch === "#" && (i === 0 || /[\s;|&(]/.test(command[i - 1]))) return null;
254
+ if (ch === "'" || ch === '"') {
255
+ quote = ch;
256
+ open = i;
257
+ }
258
+ }
259
+ return null;
260
+ }
261
+
262
+ /**
263
+ * True for the jq alternative operator (`//`, `//=`) used as its own word inside
264
+ * a quoted jq program: `jq '.a // "x"' f.json`, `gh ... --jq '(.x) // "none"'`.
265
+ * Narrow on purpose. The token must be whitespace-delimited, sit inside a
266
+ * plain-quoted word in a shell segment whose command word is `jq`, or is `gh`
267
+ * with the word given to `--jq`/`-q`. Unquoted tokens (`find // -x`),
268
+ * quoted bare roots (`ls "//"`) and `//` in other quoted text stay screened.
269
+ */
270
+ function isJqAlternativeOperator(command, start, token) {
271
+ if (token !== "//" && token !== "//=") return false;
272
+ const next = command[start + token.length];
273
+ if (!/\s/.test(command[start - 1] ?? "") || next === undefined || !/\s/.test(next)) return false;
274
+ const span = quotedSpanAt(command, start);
275
+ if (!span) return false;
276
+ const segment = command.slice(span[2], span[0]);
277
+ const commandWord = /^\s*\(?\s*(?:[A-Za-z_]\w*=\S*\s+)*/;
278
+ if (new RegExp(commandWord.source + "jq\\s").test(segment)) return true;
279
+ return (
280
+ new RegExp(commandWord.source + "gh\\s").test(segment) && /\s(?:--jq|-q)(?:=|\s+)$/.test(segment)
281
+ );
282
+ }
283
+
217
284
  /**
218
285
  * True when a "/"-leading token is comment or markup text rather than a path:
219
286
  * a closing tag (`</p>`), a block-comment opener (`/**`, `/*`), or a line
@@ -222,12 +289,16 @@ function isInsideOpeningQuote(command, start) {
222
289
  * checked, `/*` and `//` only count at a comment position (line start, right
223
290
  * after an opening quote, or after `;`) so `rm -rf /*`, `find // -name x`
224
291
  * and `rm -rf "" /*` stay blocked, and a block comment needs a closing
225
- * `*` + `/` later in the text.
292
+ * `*` + `/` later in the text. The jq alternative operator inside a quoted jq
293
+ * program is exempt under the separate, narrower rule in
294
+ * isJqAlternativeOperator.
226
295
  */
227
296
  function isCommentOrMarkupToken(command, start, token) {
228
297
  const end = start + token.length;
229
298
  const next = command[end];
230
299
 
300
+ if (isJqAlternativeOperator(command, start, token)) return true;
301
+
231
302
  if (command[start - 1] === "<" && next === ">" && /^\/[A-Za-z][\w:.-]*$/.test(token)) {
232
303
  return !fs.existsSync(token);
233
304
  }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadOpenCodeProviders = loadOpenCodeProviders;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const jsonc_parser_1 = require("jsonc-parser");
11
+ function isObject(value) {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ function merge(base, override) {
15
+ return Object.fromEntries([...new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => {
16
+ const previous = base[key];
17
+ const next = override[key];
18
+ if (!Object.hasOwn(override, key))
19
+ return [key, previous];
20
+ return [key, isObject(previous) && isObject(next) ? merge(previous, next) : next];
21
+ }));
22
+ }
23
+ /** Import operator providers only, before the adapter replaces OpenCode's paths.
24
+ * OpenCode V2 accepts legacy `provider` entries alongside native `providers`;
25
+ * let its own compatibility loader handle model capabilities and variants.
26
+ * Never discover repository config or import its plugins, MCP, or permissions.
27
+ */
28
+ async function loadOpenCodeProviders(env, generation) {
29
+ const setting = (name) => env[name] ?? process.env[name];
30
+ const home = setting("HOME") || node_os_1.default.homedir();
31
+ const expand = (file) => file.startsWith("~/") ? node_path_1.default.join(home, file.slice(2)) : file;
32
+ const globalDir = node_path_1.default.join(setting("XDG_CONFIG_HOME") || node_path_1.default.join(home, ".config"), "opencode");
33
+ let result = {};
34
+ let secretIndex = 0;
35
+ // Resolve file references against their original config, then keep credentials
36
+ // out of both the generated file and OPENCODE_CONFIG_CONTENT. Existing env
37
+ // references remain references and are resolved by the OpenCode child.
38
+ async function prepare(value, directory, sensitive = false) {
39
+ if (Array.isArray(value))
40
+ return Promise.all(value.map((item) => prepare(item, directory, sensitive)));
41
+ if (isObject(value)) {
42
+ return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, item]) => [
43
+ key, await prepare(item, directory, sensitive || /^(?:api[_-]?key|token|password|secret|headers)$/i.test(key)),
44
+ ])));
45
+ }
46
+ if (typeof value !== "string")
47
+ return value;
48
+ const fromFile = /\{file:[^}]+\}/.test(value);
49
+ if (!fromFile && (!sensitive || !value || /^\{env:[^}]+\}$/.test(value)))
50
+ return value;
51
+ // Mixed strings such as "Bearer {env:TOKEN}" need expansion before moving
52
+ // the whole header into another env var; substitutions are not recursive.
53
+ let resolved = value.replace(/\{env:([^}]+)\}/g, (_match, name) => setting(name) ?? "");
54
+ for (const match of value.matchAll(/\{file:([^}]+)\}/g)) {
55
+ const file = node_path_1.default.resolve(directory, expand(match[1]));
56
+ const contents = (await node_fs_1.promises.readFile(file, "utf8")).trim();
57
+ resolved = resolved.replace(match[0], () => contents);
58
+ }
59
+ let name;
60
+ do {
61
+ name = `NAVARCH_OPENCODE_PROVIDER_${secretIndex++}`;
62
+ } while (setting(name) !== undefined);
63
+ env[name] = resolved;
64
+ return `{env:${name}}`;
65
+ }
66
+ async function include(text, source, directory) {
67
+ const errors = [];
68
+ const config = (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true });
69
+ if (errors.length || !isObject(config)) {
70
+ // Parser diagnostics can contain literal credentials; report only source.
71
+ throw new Error(`Invalid OpenCode JSON/JSONC configuration in ${source}.`);
72
+ }
73
+ const picked = {};
74
+ for (const key of generation === 1 ? ["provider"] : ["provider", "providers"]) {
75
+ if (config[key] === undefined)
76
+ continue;
77
+ if (!isObject(config[key]))
78
+ throw new Error(`OpenCode ${key} must be an object in ${source}.`);
79
+ picked[key] = await prepare(config[key], directory);
80
+ }
81
+ result = merge(result, picked);
82
+ }
83
+ async function file(filePath, optional) {
84
+ const absolute = node_path_1.default.resolve(expand(filePath));
85
+ let text;
86
+ try {
87
+ text = await node_fs_1.promises.readFile(absolute, "utf8");
88
+ }
89
+ catch (error) {
90
+ if (optional && error.code === "ENOENT")
91
+ return;
92
+ throw error;
93
+ }
94
+ await include(text, absolute, node_path_1.default.dirname(absolute));
95
+ }
96
+ for (const name of ["opencode.json", "opencode.jsonc"])
97
+ await file(node_path_1.default.join(globalDir, name), true);
98
+ const explicit = setting("OPENCODE_CONFIG");
99
+ if (explicit)
100
+ await file(explicit, false);
101
+ const directory = setting("OPENCODE_CONFIG_DIR");
102
+ if (directory) {
103
+ for (const name of ["opencode.json", "opencode.jsonc"])
104
+ await file(node_path_1.default.join(expand(directory), name), true);
105
+ }
106
+ const inline = setting("OPENCODE_CONFIG_CONTENT");
107
+ if (inline)
108
+ await include(inline, "OPENCODE_CONFIG_CONTENT", process.cwd());
109
+ return result;
110
+ }
@@ -12,12 +12,20 @@ const node_os_1 = __importDefault(require("node:os"));
12
12
  const node_path_1 = __importDefault(require("node:path"));
13
13
  const config_cjs_1 = require("../config.cjs");
14
14
  const kill_agent_cjs_1 = require("../kill-agent.cjs");
15
+ const opencode_providers_cjs_1 = require("./opencode-providers.cjs");
15
16
  /**
16
- * Headless OpenCode adapter, fixture-pinned to the OpenCode JSON/config
17
- * contract shared by V1 and V2 (verified against 2.0.6):
17
+ * Headless OpenCode adapter, fixture-pinned to the OpenCode JSON event
18
+ * contract shared by V1 and V2 (verified against 1.19.1, 2.0.6 and 2.0.17):
18
19
  *
19
20
  * opencode run --format json [--model provider/model[#variant]] < prompt
20
21
  *
22
+ * V1 (still npm's `latest`) differs in two ways the adapter must follow: its
23
+ * config takes V1 `permission` rules and flat `mcp` entries, and it rejects
24
+ * V2's `permissions`/`mcp.servers` outright ("V2 permissions are not
25
+ * supported by OpenCode V1"); and it takes the reasoning variant as
26
+ * `--variant`, reporting `model#variant` as an unknown model. A `--version`
27
+ * probe picks the dialect per session, defaulting to V2.
28
+ *
21
29
  * The prompt goes in on stdin, which `opencode run` reads as the message when
22
30
  * none is given (verified against 2.0.17). Linux rejects any single argv
23
31
  * string over 128 KiB (`spawn E2BIG`), and an argv prompt also let an
@@ -27,8 +35,9 @@ const kill_agent_cjs_1 = require("../kill-agent.cjs");
27
35
  * so each Navarch session gets an isolated XDG/config directory, disables
28
36
  * project discovery, and supplies its complete policy as both a private file
29
37
  * and the highest-precedence inline config. Provider credentials remain in
30
- * OpenCode's data store or the child environment; only MCP env references are
31
- * written to disk.
38
+ * OpenCode's data store or the child environment. Operator provider definitions
39
+ * are copied without importing unrelated global config; credential and MCP
40
+ * values use env references in the generated file.
32
41
  */
33
42
  async function runOpenCodeAdapter(options) {
34
43
  if (options.opencodeHostGuard && !options.dockerExec) {
@@ -45,16 +54,20 @@ async function runOpenCodeAdapter(options) {
45
54
  // supervisor's PWD in the inherited environment (often the home directory).
46
55
  if (!options.dockerExec && options.cwd)
47
56
  env.PWD = node_path_1.default.resolve(options.cwd);
48
- await prepareOpenCodeConfig(options.mcpConfigPath, env, Boolean(options.dockerExec));
57
+ const probe = (probeArgs) => {
58
+ const probeOptions = { ...options, env, timeoutMs: 10_000, onProcessStarted: undefined };
59
+ return options.dockerExec ? runViaDocker(probeOptions, probeArgs) : runOnHost(probeOptions, probeArgs);
60
+ };
61
+ // Neither generation loads config for --version, so the probe runs before
62
+ // the config is written in the matching dialect.
63
+ const generation = openCodeGeneration(await probe(["--version"]));
64
+ await prepareOpenCodeConfig(options.mcpConfigPath, env, Boolean(options.dockerExec), generation);
49
65
  // V2's default background service owns its own cwd, environment and MCP
50
66
  // configuration. A client process's isolated config does not isolate that
51
67
  // service. Probe help without starting a turn, so older in-process CLIs
52
68
  // remain supported and upgrades are detected on the next session.
53
69
  const runOptions = { ...options, env };
54
- const probeOptions = { ...runOptions, timeoutMs: 10_000, onProcessStarted: undefined };
55
- const help = options.dockerExec
56
- ? await runViaDocker(probeOptions, ["run", "--help"])
57
- : await runOnHost(probeOptions, ["run", "--help"]);
70
+ const help = await probe(["run", "--help"]);
58
71
  if (help.exitCode !== 0 || help.timedOut || help.killedByLeaseLoss) {
59
72
  return { ...help, stderr: `OpenCode CLI capability check failed. ${help.stderr}` };
60
73
  }
@@ -76,10 +89,18 @@ async function runOpenCodeAdapter(options) {
76
89
  if (options.model && options.model !== "default" && !hasArg(options.extraArgs, "--model", "-m")) {
77
90
  // OpenCode V2 folds the reasoning variant into the model reference
78
91
  // (`provider/model#variant`) and hard-fails on a variant the model does
79
- // not advertise. The claim route clamps the effort to the model's
80
- // advertised variants, so a present value is always valid; absent means
81
- // use the model's own default.
82
- args.push("--model", options.reasoningEffort ? `${options.model}#${options.reasoningEffort}` : options.model);
92
+ // not advertise; V1 takes it as `--variant`. The claim route clamps the
93
+ // effort to the model's advertised variants, so a present value is
94
+ // always valid; absent means use the model's own default.
95
+ if (generation === 1) {
96
+ args.push("--model", options.model);
97
+ if (options.reasoningEffort && !hasArg(options.extraArgs, "--variant")) {
98
+ args.push("--variant", options.reasoningEffort);
99
+ }
100
+ }
101
+ else {
102
+ args.push("--model", options.reasoningEffort ? `${options.model}#${options.reasoningEffort}` : options.model);
103
+ }
83
104
  }
84
105
  const run = (runArgs) => runOptions.dockerExec
85
106
  ? runViaDocker(runOptions, runArgs, options.prompt)
@@ -110,10 +131,20 @@ function isLogLevelRejection(stderr) {
110
131
  function resetOpenCodeLogLevelCache() {
111
132
  openCodeLogLevels.clear();
112
133
  }
134
+ /**
135
+ * V1 prints a bare `1.19.1`, V2 `opencode v2.0.17`. Anything unrecognized
136
+ * keeps the V2 dialect the adapter has always written.
137
+ */
138
+ function openCodeGeneration(version) {
139
+ if (version.exitCode !== 0)
140
+ return 2;
141
+ const major = version.stdout.match(/^\s*(?:opencode\s+)?v?(\d+)\.\d+\.\d+/i)?.[1];
142
+ return major !== undefined && Number(major) < 2 ? 1 : 2;
143
+ }
113
144
  function hasArg(args, ...flags) {
114
145
  return args.some((arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
115
146
  }
116
- async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker) {
147
+ async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker, generation) {
117
148
  if (!mcpConfigPath) {
118
149
  throw new Error("OpenCode requires a per-session MCP config path for isolated execution.");
119
150
  }
@@ -143,28 +174,51 @@ async function prepareOpenCodeConfig(mcpConfigPath, env, inDocker) {
143
174
  mcp[name] = {
144
175
  type: "remote",
145
176
  url,
146
- disabled: false,
177
+ ...(generation === 1 ? { enabled: true } : { disabled: false }),
147
178
  oauth: false,
148
179
  ...(Object.keys(headers).length > 0 ? { headers } : {}),
149
180
  };
150
181
  }
151
- const config = {
152
- $schema: "https://opencode.ai/config.json",
153
- update: "disable",
154
- share: "disabled",
155
- plugins: [],
156
- mcp: { servers: mcp },
157
- permissions: [
158
- { action: "*", resource: "*", effect: "allow" },
159
- { action: "external_directory", resource: "*", effect: "deny" },
160
- ...(await openCodeManagedDirectories(env, inDocker)).map((resource) => ({
161
- action: "external_directory",
162
- resource,
163
- effect: "allow",
164
- })),
165
- { action: "question", resource: "*", effect: "deny" },
166
- ],
167
- };
182
+ const managedDirectories = await openCodeManagedDirectories(env, inDocker);
183
+ // Host provider endpoints and credentials belong to the operator's host.
184
+ // Docker continues to use its own provider setup, never the host's files.
185
+ const providers = inDocker ? {} : await (0, opencode_providers_cjs_1.loadOpenCodeProviders)(env, generation);
186
+ // Same policy in both dialects; in each the last matching rule wins.
187
+ const config = generation === 1
188
+ ? {
189
+ $schema: "https://opencode.ai/config.json",
190
+ ...providers,
191
+ autoupdate: false,
192
+ share: "disabled",
193
+ plugin: [],
194
+ mcp,
195
+ permission: {
196
+ "*": "allow",
197
+ external_directory: {
198
+ "*": "deny",
199
+ ...Object.fromEntries(managedDirectories.map((resource) => [resource, "allow"])),
200
+ },
201
+ question: "deny",
202
+ },
203
+ }
204
+ : {
205
+ $schema: "https://opencode.ai/config.json",
206
+ ...providers,
207
+ update: "disable",
208
+ share: "disabled",
209
+ plugins: [],
210
+ mcp: { servers: mcp },
211
+ permissions: [
212
+ { action: "*", resource: "*", effect: "allow" },
213
+ { action: "external_directory", resource: "*", effect: "deny" },
214
+ ...managedDirectories.map((resource) => ({
215
+ action: "external_directory",
216
+ resource,
217
+ effect: "allow",
218
+ })),
219
+ { action: "question", resource: "*", effect: "deny" },
220
+ ],
221
+ };
168
222
  const configPath = `${mcpConfigPath}.opencode.json`;
169
223
  const configDir = `${mcpConfigPath}.opencode-config`;
170
224
  const xdgConfigHome = `${mcpConfigPath}.opencode-xdg`;
@@ -0,0 +1,76 @@
1
+ 3.3.0 2022-06-24
2
+ =================
3
+ - `JSONVisitor.onObjectBegin` and `JSONVisitor.onArrayBegin` can now return `false` to instruct the visitor that no children should be visited.
4
+
5
+
6
+ 3.2.0 2022-08-30
7
+ =================
8
+ - update the version of the bundled Javascript files to `es2020`.
9
+ - include all `const enum` values in the bundled JavaScript files (`ScanError`, `SyntaxKind`, `ParseErrorCode`).
10
+
11
+ 3.1.0 2022-07-07
12
+ ==================
13
+ * added new API `FormattingOptions.keepLines` : It leaves the initial line positions in the formatting.
14
+
15
+ 3.0.0 2020-11-13
16
+ ==================
17
+ * fixed API spec for `parseTree`. Can return `undefine` for empty input.
18
+ * added new API `FormattingOptions.insertFinalNewline`.
19
+
20
+
21
+ 2.3.0 2020-07-03
22
+ ==================
23
+ * new API `ModificationOptions.isArrayInsertion`: If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then `modify` will insert a new item at that location instead of overwriting its contents.
24
+ * `ModificationOptions.formattingOptions` is now optional. If not set, newly inserted content will not be formatted.
25
+
26
+
27
+ 2.2.0 2019-10-25
28
+ ==================
29
+ * added `ParseOptions.allowEmptyContent`. Default is `false`.
30
+ * new API `getNodeType`: Returns the type of a value returned by parse.
31
+ * `parse`: Fix issue with empty property name
32
+
33
+ 2.1.0 2019-03-29
34
+ ==================
35
+ * `JSONScanner` and `JSONVisitor` return lineNumber / character.
36
+
37
+ 2.0.0 2018-04-12
38
+ ==================
39
+ * renamed `Node.columnOffset` to `Node.colonOffset`
40
+ * new API `getNodePath`: Gets the JSON path of the given JSON DOM node
41
+ * new API `findNodeAtOffset`: Finds the most inner node at the given offset. If `includeRightBound` is set, also finds nodes that end at the given offset.
42
+
43
+ 1.0.3 2018-03-07
44
+ ==================
45
+ * provide ems modules
46
+
47
+ 1.0.2 2018-03-05
48
+ ==================
49
+ * added the `visit.onComment` API, reported when comments are allowed.
50
+ * added the `ParseErrorCode.InvalidCommentToken` enum value, reported when comments are disallowed.
51
+
52
+ 1.0.1
53
+ ==================
54
+ * added the `format` API: computes edits to format a JSON document.
55
+ * added the `modify` API: computes edits to insert, remove or replace a property or value in a JSON document.
56
+ * added the `allyEdits` API: applies edits to a document
57
+
58
+ 1.0.0
59
+ ==================
60
+ * remove nls dependency (remove `getParseErrorMessage`)
61
+
62
+ 0.4.2 / 2017-05-05
63
+ ==================
64
+ * added `ParseError.offset` & `ParseError.length`
65
+
66
+ 0.4.1 / 2017-04-02
67
+ ==================
68
+ * added `ParseOptions.allowTrailingComma`
69
+
70
+ 0.4.0 / 2017-02-23
71
+ ==================
72
+ * fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']`
73
+
74
+ 0.3.0 / 2017-01-17
75
+ ==================
76
+ * Updating to typescript 2.0
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Microsoft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.